package masterjava.common.configuration.setting;

import masterjava.common.configuration.store.AbstractPropertiesStore;
import masterjava.common.logging.MasterLogger;
import masterjava.common.property.Property;
import masterjava.common.util.MasterProperties;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * MasterJava.ru
 * <p/>
 * User: GKislin
 * Date: 03.03.11
 */
public class Setting {

    static final String STORE_CLASS_PROPERTIES_NAME = "storeClass.properties";

    private static final Map<String, Setting> SETTING_MAP = new ConcurrentHashMap<String, Setting>();
    protected static final MasterLogger LOGGER = MasterLogger.get(Setting.class);
    protected static final Properties STORE_CLASS_PROPERTIES = new MasterProperties(STORE_CLASS_PROPERTIES_NAME);

    protected final String name;
    protected AbstractPropertiesStore propStore;

    public Setting(String name) {
        this.name = name;
        SETTING_MAP.put(name, this);
    }

    protected void initPropStore() {
        String storeClassName = STORE_CLASS_PROPERTIES.getProperty(name);
        if (storeClassName == null) {
            throw LOGGER.getIllegalStateException("Properties Store " + name + " doesn't declared in " + STORE_CLASS_PROPERTIES_NAME);
        }
        try {
            // AbstractPropertiesStore must have constructor with String argument
            propStore = (AbstractPropertiesStore) Class.forName(storeClassName).
                    getConstructor(String.class).newInstance(name);
        } catch (Exception e) {
            throw LOGGER.getIllegalStateException("AbstractPropertiesStore " + storeClassName + " coudn't be instantiated.", e);
        }
    }

    public Property<String> createProp(Enum key) throws IllegalArgumentException {
        return createProp(key.name(), String.class);
    }

    public <T> Property<T> createProp(Enum key, Class<T> clazz) throws IllegalArgumentException {
        return createProp(key.name(), clazz);
    }

    public <T> Property<T> createProp(Enum key, T defValue) throws IllegalArgumentException {
        return createProp(key.name(), defValue);
    }

    public <T> Property<T> createProp(String key, Class<T> clazz) throws IllegalArgumentException {
        return propStore.createProp(key, null, clazz);
    }

    public <T> Property<T> createProp(String key, T defValue) throws IllegalArgumentException {
        return propStore.createProp(key, defValue, (Class<T>) defValue.getClass());
    }

    public static <T extends Setting> T get(String name, Class<T> clazz) {
        Setting setting = SETTING_MAP.get(name);
        try {
            return (T) (setting == null ? clazz.newInstance() : setting);
        } catch (Exception e) {
            throw LOGGER.getIllegalStateException("Settings " + clazz.toString() + " coudn't be instantiated", e);
        }
    }

    public static void reset() {
        SETTING_MAP.clear();
    }
}
